package com.wuzheng.wisgo.sanitation.framework.util;

import com.wuzheng.wisgo.sanitation.framework.util.model.GPS;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author：hibon
 * @date：Created in 2019/10/09
 * @description：坐标转换工具类
 * * WGS84: Google Earth采用，Google Map中国范围外使用
 * * GCJ02: 火星坐标系，中国国家测绘局制定的坐标系统，由WGS84机密后的坐标。Google Map中国和搜搜地图使用，高德
 * * BD09:百度坐标，GCJ02机密后的坐标系
 * * 搜狗坐标系，图吧坐标等，估计也是在GCJ02基础上加密而成的
 * @modified By：
 * @version: 1.0
 */
public class GPSConverterUtils {
    public static final String BAIDU_LBS_TYPE = "bd09ll";
    public static double pi = 3.1415926535897932384626;
    public static double a = 6378245.0;
    public static double ee = 0.00669342162296594323;

    /**
     * 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
     *
     * @param lat
     * @param lon
     */
    public static GPS gps84ToGcj02(double lat, double lon) {
        if (outOfChina(lat, lon)) {
            return null;
        }
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return new GPS(retain9(mgLat), retain9(mgLon));
    }

    /**
     * * 火星坐标系 (GCJ-02) to 84 * * @param lon * @param lat * @return
     */
    public static GPS gcjToGps84(double lat, double lon) {
        GPS gps = transform(lat, lon);
        double lontitude = lon * 2 - gps.getLon();
        double latitude = lat * 2 - gps.getLat();
        return new GPS(retain9(latitude), retain9(lontitude));
    }

    /**
     * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
     *
     * @param gg_lat
     * @param gg_lon
     */
    public static GPS gcj02ToBd09(double gg_lat, double gg_lon) {
        double x = gg_lon, y = gg_lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * pi);
        double bd_lon = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        return new GPS(retain9(bd_lat), retain9(bd_lon));
    }

    /**
     * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 BD-09 坐标转换成GCJ-02 坐标
     *
     * @param bd_lat
     * @param bd_lon
     * @return
     */
    public static GPS bd09ToGcj02(double bd_lat, double bd_lon) {
        double x = bd_lon - 0.0065, y = bd_lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * pi);
        double gg_lon = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        return new GPS(retain9(gg_lat), retain9(gg_lon));
    }

    /**
     * (BD-09)-->84
     *
     * @param bd_lat
     * @param bd_lon
     * @return
     */
    public static GPS bd09ToGps84(double bd_lat, double bd_lon) {

        GPS gcj02 = bd09ToGcj02(bd_lat, bd_lon);
        GPS map84 = gcjToGps84(gcj02.getLat(), gcj02.getLon());
        return map84;

    }

    /**
     * is or not outOfChina
     *
     * @param lat
     * @param lon
     * @return
     */
    public static boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }

    public static GPS transform(double lat, double lon) {
        if (outOfChina(lat, lon)) {
            return new GPS(lat, lon);
        }
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return new GPS(retain9(mgLat), retain9(mgLon));
    }

    public static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
                + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }


    public static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
                * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0
                * pi)) * 2.0 / 3.0;
        return ret;
    }

    /**保留小数点后六位
     * @param num
     * @return
     */
    private static double retain9(double num){
        String result = String .format("%.9f", num);
        return Double.valueOf(result);
    }

    public static void main(String[] args) {
        List<GPS> gpsList = Arrays.asList(
                new GPS(35.780399998, 119.178005618),
                new GPS(35.780337260, 119.177716016),
                new GPS(35.780385267, 119.177716037),
                new GPS(35.780336091, 119.177780364),
                new GPS(35.780383073, 119.177836691),
                new GPS(35.780365314, 119.177933207),
                new GPS(35.780416294, 119.177989538),
                new GPS(35.780419664, 119.177804533),
                new GPS(35.780376909, 119.178174529),
                new GPS(35.780264157, 119.178214697),
                new GPS(35.761114273, 119.223754479),
                new GPS(35.780265629, 119.178134258)
        );
        for (int i =0;i<gpsList.size();i++){
            GPS gps = gps84ToGcj02(gpsList.get(i).getLat(),gpsList.get(i).getLon());
            System.out.println(gps.getLon()+"----"+gps.getLat());
        }
    }
}
